package phases

import (
	"context"
	"strings"
	"time"

	"github.com/pkg/errors"
	confv1beta1 "gopkg.openfuyao.cn/bkecommon/cluster/api/v1beta1"
	bkenode "gopkg.openfuyao.cn/bkecommon/cluster/node"
	"k8s.io/apimachinery/pkg/util/wait"
	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
	"sigs.k8s.io/cluster-api/util/conditions"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"

	bkev1beta1 "gopkg.openfuyao.cn/cluster-api-provider-bke/api/v1beta1"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/pkg/command"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/pkg/mergecluster"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/pkg/phaseframe"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/pkg/phaseframe/phaseutil"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/utils"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/utils/condition"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/utils/constant"
	labelhelper "gopkg.openfuyao.cn/cluster-api-provider-bke/utils/label"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/utils/log"
)

const (
	EnsureMasterInitName confv1beta1.BKEClusterPhase = "EnsureMasterInit"
)

type EnsureMasterInit struct {
	phaseframe.BasePhase
}

func NewEnsureMasterInit(ctx *phaseframe.PhaseContext) phaseframe.Phase {
	base := phaseframe.NewBasePhase(ctx, EnsureMasterInitName)
	return &EnsureMasterInit{base}
}
func (e *EnsureMasterInit) ExecutePreHook() error {
	return e.BasePhase.DefaultPreHook()
}

func (e *EnsureMasterInit) Execute() (_ ctrl.Result, err error) {
	nodes := bkenode.Nodes(e.Ctx.BKECluster.Spec.ClusterConfig.Nodes).Master()
	if len(nodes) == 0 {
		log.Warn(constant.MasterNotInitReason, "no master node")
		return ctrl.Result{}, errors.Errorf("no master node")
	}

	count := 0
	for _, node := range nodes {
		if !e.Ctx.BKECluster.GetNodeStateFlag(node.IP, bkev1beta1.NodeEnvFlag) {
			count++
		}
	}

	if count == nodes.Length() {
		log.Warn(constant.MasterNotInitReason, "all master node not ready,cannot init")
		return ctrl.Result{}, errors.Errorf("all master node agent is not ready")
	}

	condition.ConditionMark(e.Ctx.BKECluster, bkev1beta1.ControlPlaneInitializedCondition, confv1beta1.ConditionFalse, constant.MasterNotInitReason, "Master still not init")

	if err := mergecluster.SyncStatusUntilComplete(e.Ctx.Client, e.Ctx.BKECluster); err != nil {
		log.Error(constant.MasterNotInitReason, "failed to add %q status false", bkev1beta1.ControlPlaneInitializedCondition)
	}
	if err := e.Ctx.RefreshCtxBKECluster(); err != nil {
		return ctrl.Result{}, err
	}

	defer func() {
		// 在最后退出时，只要没有init成功，需要加上condition，来防止环境初始化command清除已经init完成的部分
		if derr := e.Ctx.RefreshCtxCluster(); derr != nil {
			e.Ctx.Log.Error(constant.MasterNotInitReason, "Get ClusterAPI Cluster obj failed, err: %v", derr)
			err = derr
		}
		if e.Ctx.Cluster != nil && !conditions.IsTrue(e.Ctx.Cluster, clusterv1.ControlPlaneInitializedCondition) {
			condition.ConditionMark(e.Ctx.BKECluster, bkev1beta1.ControlPlaneInitializedCondition, confv1beta1.ConditionFalse, constant.MasterNotInitReason, "Master still not init")
			if derr := mergecluster.SyncStatusUntilComplete(e.Ctx.Client, e.Ctx.BKECluster); derr != nil {
				e.Ctx.Log.Error(constant.MasterNotInitReason, "failed to add %q status false, err: %v", bkev1beta1.ControlPlaneInitializedCondition, derr)
				err = derr
			}

		}
	}()

	timeOut, err := phaseutil.GetBootTimeOut(e.Ctx.BKECluster)
	if err != nil {
		log.Warn(constant.MasterNotInitReason, "Get boot timeout failed. err: %v", err)
	}

	ctx, cancel := context.WithTimeout(e.Ctx.Context, timeOut)
	defer cancel()

	commandComplateFlag := false
	machineBootFlag := false
	pollCount := 0
	initNodeIp := ""

	err = wait.PollImmediateUntil(1*time.Second, func() (bool, error) {
		_, c, bkeCluster, _, log := e.Ctx.Untie()
		pollCount++

		if err = e.Ctx.RefreshCtxCluster(); err != nil {
			log.Error(constant.InternalErrorReason, "Refresh ClusterAPI Cluster obj %q failed, err: %v", utils.ClientObjNS(e.Ctx.Cluster), err)
			return false, err
		}
		if err = e.Ctx.RefreshCtxBKECluster(); err != nil {
			log.Error(constant.InternalErrorReason, "Refresh BKECluster obj %q failed, err: %v", utils.ClientObjNS(e.Ctx.BKECluster), err)
			return false, err
		}

		if conditions.IsTrue(e.Ctx.Cluster, clusterv1.ControlPlaneInitializedCondition) {
			log.Info(constant.MasterInitReason, "ClusterAPI Cluster obj already initialized")
			condition.ConditionMark(e.Ctx.BKECluster, bkev1beta1.ControlPlaneInitializedCondition, confv1beta1.ConditionTrue, "", "")
			return true, nil
		}

		// step 1 等待init command 完成
		if !commandComplateFlag {
			// 需要拿到init的command
			initCommand, err := phaseutil.GetMasterInitCommand(ctx, c, bkeCluster)
			if err != nil {
				if strings.Contains(err.Error(), "command not found") {
					// 循环十次输出一次日志
					if pollCount%10 == 0 {
						log.Info(constant.MasterNotInitReason, "Waiting init command to be created, info:%v", err)
					}
					return false, nil
				}
				return false, err
			}

			if initCommand.Spec.NodeName != "" {
				initNodeIp = initCommand.Spec.NodeName
			} else {
				// only one k,v in initCommand.Spec.NodeSelector.MatchLabels
				for k, _ := range initCommand.Spec.NodeSelector.MatchLabels {
					initNodeIp = k
					break
				}
			}

			complete, successNodes, failedNodes := command.CheckCommandStatus(initCommand)
			if complete {
				if len(failedNodes) != 0 {
					// 等两秒
					time.Sleep(2 * time.Second)
					if err = e.Ctx.RefreshCtxBKECluster(); err != nil {
						log.Error(constant.InternalErrorReason, "Refresh BKECluster obj %q failed, err: %v", utils.ClientObjNS(e.Ctx.BKECluster), err)
						return false, err
					}

					if e.Ctx.BKECluster.GetNodeStateFlag(initNodeIp, bkev1beta1.NodeFailedFlag) {
						e.Ctx.BKECluster.SetNodeState(initNodeIp, bkev1beta1.NodeBootStrapFailed)
						//删除command
						if err = c.Delete(ctx, initCommand); err != nil {
							log.Warn(constant.MasterNotInitReason, "Delete init command failed, err: %v", err)
							return false, nil
						}
						return true, errors.Errorf("master node init failed, failed nodes: %v", failedNodes)
					}

					log.Warn(constant.MasterNotInitReason, "Master node init failed, failed nodes: %v", failedNodes)
					log.Info(constant.MasterNotInitReason, "Trying to init master node again...")
					// get command owner bkeMachine
					ownerBkeMachineName := initCommand.OwnerReferences[0].Name
					// update owner bkeMachine status for bootstrap again

					key := client.ObjectKey{
						Name:      ownerBkeMachineName,
						Namespace: initCommand.Namespace,
					}

					ownerBkeMachine := &bkev1beta1.BKEMachine{}
					if err := c.Get(ctx, key, ownerBkeMachine); err != nil {
						log.Error(constant.MasterNotInitReason, "Get init command owner bkeMachine failed, err: %v", err)
						return false, err
					}
					labelhelper.RemoveBKEMachineLabel(ownerBkeMachine, bkenode.MasterNodeRole)
					if err := c.Update(ctx, ownerBkeMachine); err != nil {
						log.Error(constant.MasterNotInitReason, "Update init command owner bkeMachine failed, err: %v", err)
						return false, err
					}
					return false, errors.Errorf("master node init command run failed, failed nodes: %v", failedNodes)
				}
				if len(successNodes) != 0 {
					commandComplateFlag = true
					log.Info(constant.MasterNotInitReason, "Master node init command run success, success nodes: %v", successNodes)
				}
			} else {
				// 循环十次输出一次日志
				if pollCount%10 == 0 {
					log.Info(constant.MasterNotInitReason, "Master node init command not run complete, waiting...")
				}
				return false, nil
			}
		}

		// step 2 等待bkeMachine被标记已经引导
		if !machineBootFlag {
			bkeMachine, err := phaseutil.GetControlPlaneInitBKEMachine(ctx, c, bkeCluster)
			if err != nil {
				if pollCount%10 == 0 {
					log.Error(constant.MasterNotInitReason, "get init BKEMachine failed, err: %v", err)
				}
				return false, nil
			}
			if !bkeMachine.Status.Bootstrapped {
				return false, nil
			}
			machineBootFlag = true
		}

		if !conditions.IsTrue(e.Ctx.Cluster, clusterv1.ControlPlaneInitializedCondition) {
			if pollCount%10 == 0 {
				log.Info(constant.MasterNotInitReason, "Waiting for ClusterAPI Cluster obj to be initialized")
			}
			// get Cluster obj
			return false, nil
		}

		return false, nil
	}, ctx.Done())

	if err != nil {
		if errors.Is(err, wait.ErrWaitTimeout) {
			return ctrl.Result{}, errors.Errorf("Wait master init failed")
		}
		return ctrl.Result{}, err
	}
	e.Ctx.Log.Info(constant.MasterInitReason, "Master node init success")
	return ctrl.Result{}, mergecluster.SyncStatusUntilComplete(e.Ctx.Client, e.Ctx.BKECluster)
}

func (e *EnsureMasterInit) NeedExecute(old *bkev1beta1.BKECluster, new *bkev1beta1.BKECluster) (needExecute bool) {
	if !e.BasePhase.DefaultNeedExecute(old, new) {
		return false
	}

	needExecute = true
	if err := e.Ctx.RefreshCtxCluster(); err == nil {
		if conditions.IsTrue(e.Ctx.Cluster, clusterv1.ControlPlaneInitializedCondition) {
			return false
		}
	}

	//nodes := phaseutil.GetBKENodesFromNodesStatus(new.Status.NodesStatus)
	//if old == nil && nodes.Master().Length() != 0 {
	//	needExecute = false
	//}

	if needExecute {
		e.SetStatus(bkev1beta1.PhaseWaiting)
	}
	return needExecute
}
